package com.dj.mingkong.portal.biz.service.music.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dj.mingkong.common.enums.MusicStatusEnum;
import com.dj.mingkong.common.enums.MusicianStatusEnum;
import com.dj.mingkong.common.enums.StatusEnum;
import com.dj.mingkong.common.enums.ToneTypeEnum;
import com.dj.mingkong.common.exception.ValidateException;
import com.dj.mingkong.common.infrastructure.base.dto.page.PageVo;
import com.dj.mingkong.common.infrastructure.base.eo.BaseEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.MusicEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.MusicResourceEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.MusicReviewEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.MusicianEo;
import com.dj.mingkong.common.infrastructure.eo.database.system.DictionaryEo;
import com.dj.mingkong.common.microservice.UserContext;
import com.dj.mingkong.common.util.AssertUtils;
import com.dj.mingkong.common.util.LocalDateTimeUtils;
import com.dj.mingkong.common.util.SmartBeanCopy;
import com.dj.mingkong.portal.biz.controller.music.vo.request.*;
import com.dj.mingkong.portal.biz.controller.music.vo.response.MusicResourceVo;
import com.dj.mingkong.portal.biz.controller.music.vo.response.MusicReviewVo;
import com.dj.mingkong.portal.biz.controller.music.vo.response.MusicVo;
import com.dj.mingkong.portal.biz.core.config.BaseConfig;
import com.dj.mingkong.portal.biz.repository.database.music.*;
import com.dj.mingkong.portal.biz.repository.database.system.DictionaryRepository;
import com.dj.mingkong.portal.biz.service.music.MusicService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @author zenglonghui
 * @date: 2022/8/20 20:56
 */
@Slf4j
@Service("musicService")
public class MusicServiceImpl implements MusicService {
    @Resource
    private MusicRepository musicRepository;
    @Resource
    private MusicianRepository musicianRepository;
    @Resource
    private MusicReviewRepository musicReviewRepository;
    @Resource
    private StyleRepository styleRepository;
    @Resource
    private TypeRepository typeRepository;
    @Resource
    private DictionaryRepository dictionaryRepository;
    @Resource
    private BaseConfig baseConfig;
    @Resource
    private MusicResourceRepository musicResourceRepository;

    private static final String BE_FROM = "beFrom";

    @Override
    public PageVo<MusicVo> pageByVo(MusicQueryVo queryVo) {
        final MusicianEo musicianEo = musicianRepository.oneByUserId(UserContext.getUserId());
        AssertUtils.isNotNull(musicianEo, "您未入驻音乐人，请到我的音乐人申请!");
        queryVo.setMusicianId(musicianEo.getId());
        final Page<MusicEo> eoPage = musicRepository.listPageByVo(queryVo);
        final PageVo<MusicVo> pageVo = SmartBeanCopy.copyPageVo(eoPage, MusicVo.class);
        pageVo.getRecords().forEach(vo -> buildData(vo.setMusicianName(musicianEo.getStageName())));
        return pageVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insert(MusicAddVo addVo) {
        final MusicianEo musicianEo = musicianRepository.oneByUserId(UserContext.getUserId());
        AssertUtils.isNotNull(musicianEo, "未入驻音乐人，请到我的音乐人申请!");
        if (musicRepository.countByMusicianId(musicianEo.getId()) >= baseConfig.getMusicUploadCount()) {
            throw new ValidateException("超出上传限制次数!");
        }
        checkMusicVo(addVo);
        final MusicEo musicEo = SmartBeanCopy.copy(addVo, MusicEo.class);
        musicRepository.save(musicEo.setMusicianId(musicianEo.getId()));
        saveMusicResource(musicEo.getId(), addVo.getMusicResourceAddVoList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateById(MusicUpdateVo updateVo) {
        final MusicEo musicEo = musicRepository.getById(updateVo.getId());
        AssertUtils.isNotNull(musicEo, "MusicEo invalid!");
        if (!MusicStatusEnum.PENDING.equalsIntVal(musicEo.getStatus())
                && !MusicStatusEnum.TO_UPDATE.equalsIntVal(musicEo.getStatus())) {
            throw new ValidateException("Status unsatisfied!");
        }
        final MusicianEo musicianEo = musicianRepository.oneByUserId(UserContext.getUserId());
        AssertUtils.isNotNull(musicianEo, "未入驻音乐人，请到我的音乐人申请!");
        if (!musicianEo.getId().equals(musicEo.getMusicianId())) {
            throw new ValidateException("No permission!");
        }
        checkMusicVo(updateVo);
        final MusicEo eo = SmartBeanCopy.copy(updateVo, MusicEo.class);
        musicRepository.updateById(eo);
        updateMusicResource(musicEo.getId(), updateVo.getMusicResourceUpdateVoList());
    }

    /*@Override
    public SearchHits<MusicEsEo> searchByVo(MusicQueryEsVo queryVo) {
        //设置查询分页
        final PageRequest pageRequest = PageRequest.of(queryVo.getPage() - 1, queryVo.getPageSize(), WrapperUtil.buildEsSort(queryVo.getSort()));
        //构建查询条件
        final BoolQueryBuilder boolQueryBuilder = buildBoolQueryBuilder(queryVo);
        //设置高亮字段
        final HighlightBuilder highlightBuilder = buildHighlightBuilder();
        // 构建查询
        final NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withHighlightBuilder(highlightBuilder)
                .withPageable(pageRequest)
                .build();
        //执行查询
        return elasticsearchRestTemplate.search(nativeSearchQuery, MusicEsEo.class);
    }

    @Override
    public SearchHits<MusicEsEo> searchLikeName(String name) {
        //设置查询分页
        final PageRequest pageRequest = PageRequest.of(0, CommonConstants.DEFAULT_PAGE_SIZE, WrapperUtil.buildEsSort(""));
        //构建查询条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.termQuery("status", MusicStatusEnum.PASS.getIntVal()))
                .must(QueryBuilders.fuzzyQuery("name", name).fuzziness(Fuzziness.ONE));
        // 构建查询
        final NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(pageRequest)
                .build();
        //执行查询
        return elasticsearchRestTemplate.search(nativeSearchQuery, MusicEsEo.class);
    }

    private HighlightBuilder buildHighlightBuilder() {
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置标签前缀
        highlightBuilder.preTags("<font color='#00F7DE'>");
        //设置标签后缀
        highlightBuilder.postTags("</font>");
        //设置高亮字段
        highlightBuilder.field("name");
        return highlightBuilder;
    }

    private BoolQueryBuilder buildBoolQueryBuilder(MusicQueryEsVo queryVo) {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.termQuery("status", MusicStatusEnum.PASS.getIntVal()))
                .must(QueryBuilders.fuzzyQuery("name", queryVo.getName()).fuzziness(Fuzziness.ONE));
        if (CollectionUtils.isNotEmpty(queryVo.getTypeIdList())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("typeId", queryVo.getTypeIdList()));
        }

        if (CollectionUtils.isNotEmpty(queryVo.getStyleIdList())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("styleId", queryVo.getStyleIdList()));
        }

        if (CollectionUtils.isNotEmpty(queryVo.getLanguageKeyList())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("languageKey", queryVo.getLanguageKeyList()));
        }

        if (CollectionUtils.isNotEmpty(queryVo.getMusicClassifyList())) {
            for (String classify : queryVo.getMusicClassifyList()) {
                boolQueryBuilder.must(QueryBuilders.termQuery(classify,
                        BE_FROM.equals(classify) ? StatusEnum.INVALID.getIntVal() : StatusEnum.VALID.getIntVal()));
            }
        }
        return boolQueryBuilder;
    }*/

    private void updateMusicResource(Long musicId, List<MusicResourceUpdateVo> musicResourceVoList) {
        if (CollectionUtils.isNotEmpty(musicResourceVoList)) {
            final List<MusicResourceEo> resourceEos = musicResourceRepository.listByMusicId(musicId);
            if (resourceEos.isEmpty()) {
                addResource(musicId, musicResourceVoList);
            } else {
                final List<Integer> typeList = resourceEos.stream().map(MusicResourceEo::getToneType).toList();
                final List<MusicResourceUpdateVo> addList = musicResourceVoList.stream()
                        .filter(vo -> Objects.isNull(vo.getId()) && !typeList.contains(vo.getToneType())).toList();
                addResource(musicId, addList);
                final List<MusicResourceUpdateVo> updateList = musicResourceVoList.stream()
                        .filter(vo -> Objects.nonNull(vo.getId())).toList();
                updateResource(musicId, updateList);
                if (CollectionUtils.isNotEmpty(updateList)) {
                    final List<Long> longs = updateList.stream().map(MusicResourceUpdateVo::getId).toList();
                    final List<Long> delList = resourceEos.stream()
                            .map(MusicResourceEo::getId).filter(id -> !longs.contains(id)).toList();
                    musicResourceRepository.deleteByIds(delList);
                }
            }
        }
    }

    private void saveMusicResource(Long musicId, List<MusicResourceAddVo> musicResourceVoList) {
        final List<MusicResourceEo> musicResourceEos = musicResourceVoList.stream()
                .map(vo -> buildResourceEo(vo, musicId))
                .filter(eo -> StringUtils.isNotBlank(eo.getMusicUrl())
                        && !ToneTypeEnum.TEST.equalsType(eo.getToneType())).toList();
        AssertUtils.isNotNull(musicResourceEos, "musicResourceEos can not be null!");
        final long count = musicResourceEos.stream().map(MusicResourceEo::getToneType).distinct().count();
        if (musicResourceEos.size() != count) {
            throw new ValidateException("toneType invalid!");
        }
        musicResourceRepository.saveBatch(musicResourceEos);
    }

    private void addResource(Long musicId, List<MusicResourceUpdateVo> musicResourceVoList) {
        if (CollectionUtils.isNotEmpty(musicResourceVoList)) {
            final List<MusicResourceEo> musicResourceEos = musicResourceVoList.stream()
                    .map(vo -> buildResourceEo(vo, musicId))
                    .filter(eo -> StringUtils.isNotBlank(eo.getMusicUrl())
                            && !ToneTypeEnum.TEST.equalsType(eo.getToneType())).toList();
            if (CollectionUtils.isNotEmpty(musicResourceEos)) {
                final long count = musicResourceEos.stream().map(MusicResourceEo::getToneType).distinct().count();
                if (musicResourceEos.size() != count) {
                    throw new ValidateException("toneType invalid!");
                }
                musicResourceRepository.saveBatch(musicResourceEos);
            }
        }
    }

    private void updateResource(Long musicId, List<MusicResourceUpdateVo> musicResourceVoList) {
        if (CollectionUtils.isNotEmpty(musicResourceVoList)) {
            final List<MusicResourceEo> musicResourceEos = musicResourceVoList.stream()
                    .map(vo -> buildResourceEo(vo, musicId))
                    .filter(eo -> StringUtils.isNotBlank(eo.getMusicUrl())).toList();
            if (CollectionUtils.isNotEmpty(musicResourceEos)) {
                musicResourceEos.forEach(this::checkToneType);
                musicResourceRepository.updateBatchById(musicResourceEos);
            }
        }
    }

    private void checkToneType(MusicResourceEo musicResourceEo) {
        final MusicResourceEo resourceEo = musicResourceRepository.getById(musicResourceEo.getId());
        AssertUtils.isNotNull(resourceEo, "id invalid!");
        if (!musicResourceEo.getToneType().equals(resourceEo.getToneType())) {
            throw new ValidateException("toneType not allow update!");
        }
    }

    private MusicResourceEo buildResourceEo(MusicResourceAddVo vo, Long musicId) {
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(vo.getToneKey()), "toneKey invalid!");
        final MusicResourceEo musicResourceEo = SmartBeanCopy.copy(vo, MusicResourceEo.class);
        if (!baseConfig.getMusicFormat().contains(musicResourceEo.getFormat())) {
            throw new ValidateException("format invalid");
        }
        musicResourceEo.setMusicId(musicId);
        return musicResourceEo;
    }

    private MusicResourceEo buildResourceEo(MusicResourceUpdateVo vo, Long musicId) {
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(vo.getToneKey()), "toneKey invalid!");
        final MusicResourceEo musicResourceEo = SmartBeanCopy.copy(vo, MusicResourceEo.class);
        if (!baseConfig.getMusicFormat().contains(musicResourceEo.getFormat())) {
            throw new ValidateException("format invalid");
        }
        musicResourceEo.setMusicId(musicId);
        return musicResourceEo;
    }

    private void checkMusicVo(MusicAddVo addVo) {
        AssertUtils.isNotNull(typeRepository.getById(addVo.getTypeId()), "typeId invalid");
        AssertUtils.isNotNull(styleRepository.getById(addVo.getStyleId()), "styleId invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(addVo.getLanguageKey()), "languageKey invalid");
    }

    private void checkMusicVo(MusicUpdateVo updateVo) {
        AssertUtils.isNotNull(typeRepository.getById(updateVo.getTypeId()), "typeId invalid");
        AssertUtils.isNotNull(styleRepository.getById(updateVo.getStyleId()), "styleId invalid");
        AssertUtils.isNotNull(dictionaryRepository.oneByFullKey(updateVo.getLanguageKey()), "languageKey invalid");
    }

    private void buildData(MusicVo vo) {
        final List<MusicReviewEo> musicReviewEos = musicReviewRepository.listByMusicId(vo.getId());
        final List<MusicResourceEo> musicResourceEos = musicResourceRepository.listByMusicId(vo.getId());
        vo.setMusicReviewVoList(SmartBeanCopy.copy(musicReviewEos, MusicReviewVo.class))
                .setMusicResourceVoList(SmartBeanCopy.copy(musicResourceEos, MusicResourceVo.class));
    }

}
